<?php

Loader::using("underscore::core::util::Message");

/**
 * A module represents a single use case in the business logic.
 * 
 */
abstract class Module {

    private static $AllModulesMetadata = array();
    private $moduleMetadata;
    private $lastUnhandledException;
    /**
     *
     * @var string The name of the application this module belongs to.
     */
    private $appName = "";
    /**
     * This module name.
     * @var string
     */
    private $name;
    /**
     * The templates manager associated to this module.
     * 
     * @var TemplatesManager
     */
    private $view;
    private $rendered = null;
    private $currentTask;
    private $defaultTask = "index";
    /**
     *
     * @var array Data associated to this module.
     */
    private $module = array("messages" => array());
    /**
     *
     * @var Principal
     */
    protected $currentUser;
    protected $loaders = array();
    /**
     *
     * @var Routing
     */
    protected $route;
    /**
     *
     * @var Request
     */
    protected $request;

    /**
     * Adds a variable to the view.
     * 
     * @param string $name
     * @param mixed $value 
     * @see TemplatesManager->assign()
     */
    protected function assign($name, $value) {
        $this->view->assign($name, $value);
    }

    /**
     * Executes a task
     * 
     * @param string $task The task name
     * @return string The result of the execution.
     */
    public function execute($task) {
        foreach ($this->loaders as &$loader) {
            $loader->setModuleName($this->name);
            $loader->setApp($this->appName);
        }

        $this->currentTask = $this->defaultTask;

        if (method_exists($this, $task)) {
            $this->currentTask = $task;
        }

        $this->{$this->currentTask}();
        return $this->rendered;
    }

    protected function display($view = null) {
        if ($view === null) {
            $view = $this->currentTask;
        }
        //Sets this single module metadata.
        $this->module["meta"] = $this->getMetadata();
        $this->module["appName"] = $this->appName;
        $this->module["name"] = $this->name;
        $this->module["context"] = ModulesContext::instance();
        $this->module["views"] = "{$this->getModuleDir()}" . DS . "views" . DS;

        $this->assign("Module", $this->module);
        $this->assign("Modules", self::$AllModulesMetadata);

        $this->rendered = $this->view->display($this->getModuleDir(), $view);
    }

    protected function addMessage(Message $message) {
        $this->module["messages"][] = $message;
    }

    public function getLoaders() {
        return $this->loaders;
    }

    /**
     *
     * @param array $loaders
     *
     * @import-many Loader
     */
    public function setLoaders($loaders) {
        foreach ($loaders as &$loader) {
            if ($loader instanceof AbstractLoader) {
                $this->loaders[get_class($loader)] = $loader;
            }
        }
    }

    /**
     *
     * @param string $name
     * @return AbstractLoader
     */
    public function __get($name) {
        if (!array_key_exists($name . "Loader", $this->loaders)) {
            throw new Exception("There is no loader named '$name'");
        }
        return $this->loaders[$name . "Loader"];
    }

    /**
     *
     * @param TemplatesManager $templateManager
     *
     * @import TemplatesManager
     */
    public function setTemplateManager(TemplatesManager &$templateManager) {
        $this->view = $templateManager;
    }

    /**
     *
     * @param Principal $currentUser
     *
     * @import User
     */
    public function setCurrentUser(Principal &$currentUser) {
        $this->currentUser = $currentUser;

        foreach ($this->loaders as &$loader) {
            $loader->setCurrentUser($this->currentUser);
        }
    }

    /**
     *
     * @param Routing $route
     *
     * @import Router
     */
    public function setRoute(Routing &$route) {
        $this->route = $route;
        $this->route->setModuleName($this->name);
    }

    public function setDefaultTask($defaultTask) {
        $this->defaultTask = $defaultTask;
    }

    /**
     *
     * @param Request $request
     *
     * @import RequestData
     */
    public function setRequest(Request &$request) {
        $this->request = $request;
    }

    function __construct() {
        $this->name = get_class($this);
    }

    public function getModuleDir() {
        $dir = Loader::toPaths("$this->appName::$this->name::", "");
        return $dir[0];
    }

    public function getName() {
        return $this->name;
    }

    public abstract function index();

    /**
     *
     * @return MetadataCollection
     */
    public function getMetadata() {
        if ($this->moduleMetadata == null) {
            if (array_key_exists($this->name, self::$AllModulesMetadata)) {
                $this->moduleMetadata = new MetadataCollection(self::$AllModulesMetadata[$this->getName()]);
            } else {
                $this->moduleMetadata = new MetadataCollection(array());
            }
        }

        return $this->moduleMetadata;
    }

    public static function setAllModulesMetadata(&$AllModulesMetadata) {
        self::$AllModulesMetadata = $AllModulesMetadata;
    }

    public static function getAllModulesMetadata() {
        return self::$AllModulesMetadata;
    }

    public function getAppName() {
        return $this->appName;
    }

    public function setAppName($appName) {
        $this->appName = $appName;
        $this->route->setAppName($this->appName);
    }

    /**
     *
     * @return UnderscoreException 
     */
    public function getLastUnhandledException() {
        return $this->lastUnhandledException;
    }

    public function setLastUnhandledException(UnderscoreException $lastUnhandledException) {
        $this->lastUnhandledException = $lastUnhandledException;
    }

}

?>
